

#include "stdafx.h" 
#include <cemapi.h> 
#include <mapiutil.h>
#include <atlbase.h>
//#include "resource.h" 
#include "SMSHelper.h"
#include "cemapi.h"
#pragma comment(lib, "cemapi.lib")

// SMSHelper.cpp : Defines the entry point for the DLL application.
//


///////////////////////////////////////////////////////////////////////////////////////////// 
// This function is used to get the msgstore named SMS from msgstores on the 
// device. 
// I could have used just raw pointers but it is much easier and safer to just 
// use smart pointers. 
HRESULT GetSMSMsgStore(const CComPtr<IMAPISession>& spSession, CComPtr<IMsgStore>& spMsgStore) 
{ 
	// first we get the msgstores table from the session 
	CComPtr<IMAPITable> spTable; 
	HRESULT hr = spSession->GetMsgStoresTable(MAPI_UNICODE, &spTable); 
	if (FAILED(hr)) 
	{ 
		return FALSE; 
	}

	// next we loop over the message stores opening each msgstore and 
	// getting its name to see if the name matches SMS. 
	// If it does then we break out of the loop 
	while (TRUE) 
	{ 
		SRowSet* pRowSet = NULL; 
		hr = spTable->QueryRows(1, 0, &pRowSet); 

		// If we failed to query the 
		// rows then we need to break 
		if (FAILED(hr)) 
		{ 
			break; 
		} 

		// if we got no rows back then just exit the loop 
		//remembering to set an error 
		if (pRowSet->cRows == 1) 
		{ 
			ASSERT(pRowSet->aRow[0].lpProps->ulPropTag == PR_ENTRYID); 
			SBinary& blob = pRowSet->aRow[0].lpProps->Value.bin; 
			hr = spSession->OpenMsgStore(NULL, blob.cb, (LPENTRYID)blob.lpb, NULL, 0, &spMsgStore); 
			if (FAILED(hr))
			{
				break;
			}

			// now get the display name property from the 
			// message store to compare it against the name 
			// 'SMS' 
			SPropTagArray props; 
			props.cValues = 1; 
			props.aulPropTag[0] = PR_DISPLAY_NAME; 

			ULONG cValues; 
			SPropValue* pProps = NULL; 
			hr = spMsgStore->GetProps(&props, MAPI_UNICODE, &cValues, &pProps); 
			if (FAILED(hr) || cValues != 1) 
			{ 
				break; 
			} 

			// if the name matches SMS then break and as 
			// hr == S_OK the current MsgStore smart pointer 
			// will correctly be set. 
			if (_tcsicmp(pProps[0].Value.lpszW, _T("SMS")) == 0) 
			{ 
				break; 
			} 
		}
		else 
		{ 
			hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND); 
			// now remember to free the row set 
			FreeProws(pRowSet); 
			if (FAILED(hr)) 
			{ 
				break; 
			} 
		}

		spMsgStore.Release();
	}

	// if we failed for some reason then we clear out 
	// the msgstore smartpointer and return the error. 
	if (FAILED(hr)) 
	{ 
		spMsgStore.Release(); 
	} 

	return hr; 
}


///////////////////////////////////////////////////////////////////////////////////////////// 
// This function is used to get the folder named drafts from the msgstore on the 
// device. 
// I could have used just raw pointers but it is much easier and safer to just 
// use smart pointers. 
HRESULT GetSMSFolder(const CComPtr<IMsgStore>& spMsgStore, CComPtr<IMAPIFolder>& spFolder) 
{ 
	// Now get the Drafts folder. 
	SPropTagArray propDefaultFolder; 
	propDefaultFolder.cValues = 1; 
	propDefaultFolder.aulPropTag[0] = PR_IPM_OUTBOX_ENTRYID/*PR_CE_IPM_DRAFTS_ENTRYID*/; //PR_IPM_OUTBOX_ENTRYID;//

	ULONG cValues; 
	LPSPropValue pPropVals; 
	HRESULT hr = spMsgStore->GetProps (&propDefaultFolder, MAPI_UNICODE, &cValues, &pPropVals); 
	if (FAILED(hr)) 
	{ 
		return hr; 
	} 

	SBinary& eidDrafts = pPropVals->Value.bin; 

	hr = spMsgStore->OpenEntry(eidDrafts.cb, (LPENTRYID)eidDrafts.lpb, NULL, MAPI_MODIFY, NULL, (LPUNKNOWN*)&spFolder); 

	return hr; 
} 
//
//HRESULT GetOutboxFolder(const CComPtr<IMsgStore>& spMsgStore, CComPtr<IMAPIFolder>& spFolder) 
//{ 
//	// Now get the Drafts folder. 
//	SPropTagArray propDefaultFolder; 
//	propDefaultFolder.cValues = 1; 
//	propDefaultFolder.aulPropTag[0] = PR_IPM_OUTBOX_ENTRYID;
//
//	ULONG cValues; 
//	LPSPropValue pPropVals; 
//	HRESULT hr = spMsgStore->GetProps (&propDefaultFolder, MAPI_UNICODE, &cValues, &pPropVals); 
//	if (FAILED(hr)) 
//	{ 
//		return hr; 
//	} 
//
//	SBinary& eidDrafts = pPropVals->Value.bin; 
//
//	hr = spMsgStore->OpenEntry(eidDrafts.cb, (LPENTRYID)eidDrafts.lpb, NULL, MAPI_MODIFY, NULL, (LPUNKNOWN*)&spFolder); 
//
//	return hr; 
//} 

///////////////////////////////////////////////////////////////////////////////////////////// 
// This function is used to get the send the message. 
// This uses an opened MAPI session 
HRESULT SendSMSMessage(const CComPtr<IMAPISession>& spSession, LPCTSTR lpszFrom, LPCTSTR lpszTo, LPCTSTR lpszMessage, LPCTSTR lpszMsgBody,LPCTSTR lpszMessageClass) 
{ 
	// now get the SMS message store 
	CComPtr<IMsgStore> spMsgStore; 
	HRESULT hr = GetSMSMsgStore(spSession, spMsgStore); 
	if (FAILED(hr)) 
	{ 
		return hr; 
	} 

	CComPtr<IMAPIFolder> spFolder;
	hr = GetSMSFolder(spMsgStore, spFolder); 
	if (FAILED(hr)) 
	{ 
		return hr; 
	} 

	//CComPtr<IMAPIFolder> spOutboxFolder;
	//hr = GetOutboxFolder(spMsgStore, spOutboxFolder); 
	//if (FAILED(hr)) 
	//{ 
	//	return hr; 
	//}

	CComPtr<IMessage> spMessage; 
	hr = spFolder->CreateMessage(NULL, 0 ,&spMessage); 
	if (FAILED(hr)) 
	{ 
		return hr; 
	}

	// set the recipients 
	// set up the required fields for a recipient 
	SPropValue propRecipient[3] = {0}; 
	// it is vital we clear the property structure 
	// as there are fields we do not use but MAPI seems 
	// to be sentative to them. 
	//ZeroMemory(&propRecipient, sizeof(propRecipient)); 

	// set the recipient type which coul be to, cc, bcc 
	// but ehre must at least be a to field 
	propRecipient[0].ulPropTag = PR_RECIPIENT_TYPE; 
	propRecipient[0].Value.l = MAPI_TO; 

	// we set the type of address to sms instead of 
	// smtp 
	propRecipient[1].ulPropTag = PR_ADDRTYPE; 
	propRecipient[1].Value.lpszW = _T("SMS"); 
	// we finally set the email address to the 
	// phone number of the person we are sending the message 
	// to 
	propRecipient[2].ulPropTag = PR_EMAIL_ADDRESS; 
	propRecipient[2].Value.lpszW = (LPWSTR)lpszTo; 

	// set the addrlist to point to the properties 
	ADRLIST adrlist; 
	adrlist.cEntries = 1; 
	adrlist.aEntries[0].cValues = 3; 
	adrlist.aEntries[0].rgPropVals = (LPSPropValue)(&propRecipient); 

	// finally modify the recipients of the message 
	hr = spMessage->ModifyRecipients(MODRECIP_ADD, &adrlist); 

	if (FAILED(hr)) 
	{ 
		return hr; 
	} 
	else 
		; // added the recipient to the message 
	
	// now we set the additional properties for the 
	// message 
	SPropValue props[7] = {0}; 
	INT propIdx = 0;

	//note how we zero out the contents of the 
	// structure as MAPI is sensative to the 
	// contents of other fields we do not use. 
	//ZeroMemory(&props, sizeof(props)); 

	//vCard SMS
	props[propIdx].ulPropTag = PR_MESSAGE_CLASS; 
	props[propIdx].Value.lpszW = (LPWSTR)lpszMessageClass; 
	propIdx++;

	//set unicode
	LPSPropTagArray psPropArr = NULL;
	MAPINAMEID mapiNameId = {0};
	GUID PS_MAPI = {0x00020328, 0, 0, 0xC0,0,0,0,0,0,0,0x46}; 
	LPMAPINAMEID pmapiNameId = &mapiNameId;
	mapiNameId.lpguid = (LPGUID)&PS_MAPI; 
	mapiNameId.ulKind = MNID_STRING; 
	if (wcscmp (lpszMessageClass,L"IPM.SMStext.vcard") != 0)
	{
		mapiNameId.Kind.lpwstrName = L"SMS:Unicode"; 
	}
	else
	{
		mapiNameId.Kind.lpwstrName = L"SMS:vcard:Unicode";
	}
	hr = spMessage->GetIDsFromNames (1, &pmapiNameId, MAPI_CREATE, &psPropArr);
	if (FAILED(hr)) 
	{ 
		goto CLEANUP;
	}
	props[propIdx].ulPropTag = CHANGE_PROP_TYPE(psPropArr[0].aulPropTag[0], PT_BOOLEAN); 
	props[propIdx].Value.b = TRUE; 
	propIdx++;


	// finally and most importantly tell mapi 
	// this is a sms message in need of delivery 
	props[propIdx].ulPropTag = PR_MSG_STATUS;
    props[propIdx].Value.ul = MSGSTATUS_RECTYPE_SMS;
	propIdx++;

	props[propIdx].ulPropTag = PR_MESSAGE_FLAGS; 
	props[propIdx].Value.ul =  MSGFLAG_UNSENT;  //MSGFLAG_UNSENT
	propIdx++;

	// first set the subject of the message 
	// as the sms we are going to send 
	if (lpszMessage != NULL && wcslen (lpszMessage) > 0)
	{
		//TCHAR szSubject[6000] = {0};
		//StringCchCopy (szSubject,5051,lpszMessage);
		props[propIdx].ulPropTag = PR_SUBJECT;
		props[propIdx].Value.lpszW = (LPWSTR)lpszMessage;
		propIdx++;
	}

	// next set the senders email address to 
	// the phone number of the person we are 
	// sending the message to 
	props[propIdx].ulPropTag = PR_SENDER_EMAIL_ADDRESS; 
	props[propIdx].Value.lpszW = (LPWSTR)lpszFrom; 
	propIdx++;

	hr = spMessage->SetProps(propIdx, (LPSPropValue)&props, NULL); 
	if (FAILED(hr)) 
	{ 
		goto CLEANUP;
	}

	//Write message body
	//char *psText = NULL;
	if (lpszMsgBody != NULL && wcslen (lpszMsgBody) > 0)
	{
		CComPtr<IStream> spStream;
		hr = spMessage->OpenProperty(PR_BODY, NULL, 0, MAPI_MODIFY | MAPI_CREATE, (LPUNKNOWN *)&spStream);
		if (FAILED(hr)) 
		{ 
			goto CLEANUP;
		}
		/*
		DWORD dwNum = WideCharToMultiByte(CP_OEMCP,NULL,lpszMsgBody,-1,NULL,0,NULL,FALSE);	
		psText = new char[dwNum];
		WideCharToMultiByte (CP_OEMCP,NULL,lpszMsgBody,-1,psText,dwNum,NULL,FALSE);*/

		ULONG cbBody = 0;
		ULONG cbWritten = 0;
		cbBody = (wcslen(lpszMsgBody) + 1) * sizeof(WCHAR);
		//cbBody = strlen (psText) + 1;
		hr = spStream->Write (lpszMsgBody, cbBody, &cbWritten);
	}

	// having set all the required fields we can now 
	// pass the message over to the msgstore transport 
	// to be delivered. 
	hr = spMessage->SubmitMessage(0); 
	if (FAILED(hr)) 
	{ 
		goto CLEANUP;
	} 
	
	return TRUE; 

CLEANUP:
	if (NULL != psPropArr) 
	{ 
		MAPIFreeBuffer(psPropArr); 
		psPropArr = NULL; 
	} 

	//if(!psText)
	//{
	//	delete []psText;
	//}
	return hr;
} 

///////////////////////////////////////////////////////////////////////// 
// This is the function that creates the session, using the 
// from, the recipient and the message. 
// This opens the session, opens the sms message store and opens 
// the drafts folder then create a new message and sets the sender, 
// recipient and messag, then finally sends the message. 
BOOL SendSMS(LPCTSTR lpszFrom, LPCTSTR lpszTo, LPCTSTR lpszMessage, LPCTSTR lpszMsgBody, DWORD dwMessageClass)
{
	WCHAR *lpszMessageClass;
	if (dwMessageClass == SMS_NORMAL)
	{
		lpszMessageClass = L"IPM.SMStext";
	}
	else if (dwMessageClass == SMS_VCARD)
	{
		lpszMessageClass = L"IPM.SMStext.vcard";
	}
	else
	{
		return FALSE;
	}

	HRESULT hr = MAPIInitialize(NULL); 
	if (FAILED(hr)) 
	{ 
		return hr; 
	} 
	else 
		; // initialized the MAPI subsystem 

	CComPtr<IMAPISession> spSession; 

	BOOL bRet = FALSE; 

	hr = MAPILogonEx(0 ,NULL, NULL, 0, &spSession); 
	if (FAILED(hr)) 
	{ 
		return hr;
	} 
	else 
	{ 
		bRet = SUCCEEDED(SendSMSMessage(spSession, lpszFrom, lpszTo, lpszMessage, lpszMsgBody, lpszMessageClass)); 

		spSession->Logoff(0, 0, 0); 

		spSession.Release(); 
	} 

	MAPIUninitialize(); 

	return bRet; 
}